//
//  EntityTest.cpp
//  IncrementalAlgorithm
//
//  Created by Roman Kutlak on 13/03/2011.
//  Copyright 2011 Roman Kutlak. All rights reserved.
//

#include <iostream>
#include <vector>
#include <algorithm>

#include "gtest/gtest.h"

#include "Entity.h"
#include "Ontology.h"

using namespace std;
using namespace kutlak::roman::gre;

#pragma mark -
#pragma mark constructors
// Tests the default c'tor.
TEST(Entity, DefaultConstructor) 
{
    Entity e;
    EXPECT_EQ(e.Id(), -1);
    EXPECT_EQ(e.Name(), "Entity");
    EXPECT_EQ(e.Size(), 0u);
    
    Entity e2("Object1", 3);
    EXPECT_EQ(e2.Id(), 3);
    EXPECT_EQ(e2.Name(), "Object1");
    EXPECT_EQ(e2.Size(), 0u);
}

// Test the copy constructor
TEST(Entity, AltConstructor) 
{
    Entity e("Object1", 3);
    EXPECT_EQ(e.Id(), 3);
    EXPECT_EQ(e.Name(), "Object1");
    EXPECT_EQ(e.Size(), 0u);
    
    Entity e2(e);
    EXPECT_EQ(e2.Id(), 3);
    EXPECT_EQ(e2.Name(), "Object1");
    EXPECT_EQ(e2.Size(), 0u);
    
    Entity e3 = e2;
    EXPECT_EQ(e3.Id(), 3);
    EXPECT_EQ(e3.Name(), "Object1");
    EXPECT_EQ(e3.Size(), 0u);
}


#pragma mark -
#pragma mark operators

TEST(Entity, Operators) 
{
    Entity e("Object1", 3);
    EXPECT_EQ(e.Id(), 3);
    EXPECT_EQ(e.Name(), "Object1");
    EXPECT_EQ(e.Size(), 0u);
    
    Entity e2;
    e2 = e;
    EXPECT_EQ(e2.Id(), 3);
    EXPECT_EQ(e2.Name(), "Object1");
    EXPECT_EQ(e2.Size(), 0u);
    
    EXPECT_EQ(e, e2);
    EXPECT_EQ(e == e2, true);
    
    e.SetId(10);
    EXPECT_NE(e, e2);
}

#pragma mark -
#pragma mark accessors

TEST(Entity, Accessors) 
{
    Entity e;
    EXPECT_EQ(e.Id(), -1);
    EXPECT_EQ(e.Name(), "Entity");
    EXPECT_EQ(e.Size(), 0u);
    
    e.SetId(2);
    e.SetName("Object1");
    EXPECT_EQ(e.Id(), 2);
    EXPECT_EQ(e.Name(), "Object1");
    EXPECT_EQ(e.Size(), 0u);
    
    Property p("colour", "black");
    e.AddProperty(p);
    EXPECT_EQ(e.Size(), 1u);
} 

#pragma mark -
#pragma mark enquiries

TEST(Entity, Enquiries) 
{
    Entity e("Object1");
    Property p("colour", "black");
    Vindex_t s = Voc::Instance().Index("colour");
    Vindex_t black = Voc::Instance().Index("black");
    EXPECT_EQ(e.ValueForAttribute(p.Attribute()), DEFAULT_VALUE);
    
    EXPECT_EQ(e.Size(), 0u);
    EXPECT_EQ(e.HasProperty(p), false);
    
    EXPECT_EQ(e.HasAttribute(p.Attribute()), false);
    EXPECT_EQ(e.HasAttribute(s), false);
    
    EXPECT_EQ(e.HasValue(p.Value()), false);
    EXPECT_EQ(e.HasValue(black), false);
    
    e.AddProperty(p);
    EXPECT_EQ(e.Size(), 1u);
    EXPECT_EQ(e.HasProperty(p), true);
    
    EXPECT_EQ(e.HasAttribute(p.Attribute()), true);
    EXPECT_EQ(e.HasAttribute(s), true);
    
    EXPECT_EQ(e.HasValue(p.Value()), true);
    EXPECT_EQ(e.HasValue(black), true);
    
    EXPECT_EQ(e.ValueForAttribute(p.Attribute()), p.Value());
    EXPECT_EQ(e.ValueForAttribute(s), black);
    
    Property p2("colour", "yellow");
    e.AddProperty(p2);
    
    vector<Vindex_t> properties = e.ValuesForAttribute(s);
    EXPECT_EQ(properties.size(), 2u);
    
    vector<Property> props(2);
    props[0] = p;
    props[1] = p2;
    
    for (const auto& prop : props) 
    {
        auto end = properties.end(); 
        auto res = find(properties.begin(), properties.end(), prop.Value());
        EXPECT_NE(end, res);
    }
    
    e.ClearProperties();
    EXPECT_EQ(e.Size(), 0u);
    
    
    list<Property> pList;
    
    pList.push_back(p);
    pList.push_back(p2);
    
    e.AddProperties(pList);
    e.AddProperties(props);
    
    EXPECT_EQ(e.Size(), 2);
    
    for (const auto& prop : props) 
    {
        auto end = properties.end(); 
        auto res = find(properties.begin(), properties.end(), prop.Value());
        EXPECT_NE(end, res);
    }
}

TEST(Entity, Property) 
{
    Property small = Property("size", "small");
    Entity e;
    e.AddProperty(Property("size", "small"));
    e.AddProperty(Property("type", "dog"));
    e.AddProperty(Property("colour", "black"));
    
    EXPECT_EQ(true, e.HasProperty(Property("size", "small")));
    EXPECT_EQ(true, e.HasProperty(Property("type", "dog")));
    EXPECT_EQ(true, e.HasProperty(Property("colour", "black")));
    
    EXPECT_EQ(true, e.HasProperty(small));
    small.SetNegation(true);
    EXPECT_EQ(false, e.HasProperty(small));
    
    EXPECT_EQ(false, e.HasProperty(Property("att", "val")));
}

#pragma mark -
#pragma mark IO
TEST(Entity, IO) 
{
    Entity e;
    stringstream ss;
    e.ToRdf(ss);
    string s = e.ToString();
    
    EXPECT_EQ("Entity (-1) \"Entity\" : \n", s);
    EXPECT_EQ("", ss.str());
    
    e.AddProperty(Ontology::kPersonType);
    
    ss.str("");
    e.ToRdf(ss);
    s = e.ToString();
    
    EXPECT_EQ("Entity (-1) \"Entity\" : http://dbpedia.org/ontology/Person\n\t\
Property http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://dbpedia.org/ontology/Person\n", s);
    
    EXPECT_EQ("<Entity> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://dbpedia.org/ontology/Person> .\n", ss.str());

}

#pragma mark -
#pragma mark Type
TEST(Entity, Type) 
{
    Ontology::SetDefault(pOntology());
    Property type = Property(Ontology::kType, "Dog");
    Entity e;
    e.AddProperty(Property(Ontology::kType, "Thing"));
    e.AddProperty(Property(Ontology::kType, "Animal"));
    e.AddProperty(Property(Ontology::kType, "Dog"));
    
    EXPECT_EQ(type, e.Type());
    
    e.ClearProperties();
    EXPECT_EQ(0, e.Size());
    
    e.AddProperty(Property(Ontology::kType, "Dog"));
    e.AddProperty(Property(Ontology::kType, "Thing"));
    e.AddProperty(Property(Ontology::kType, "Animal"));
    
    type.SetValue("Animal");
    EXPECT_EQ(type, e.Type());
    
    e.ClearProperties();
    EXPECT_EQ(0, e.Size());
    
    pOntology onto = pOntology(new Ontology());
    Ontology::SetDefault(onto);
    onto->AddStatement(Statement("Animal", Ontology::kSubClassOf, "Thing"));
    onto->AddStatement(Statement("Dog", Ontology::kSubClassOf, "Animal"));
    //    onto->AddStatement(Statement("Dog", Ontology::kSubClassOf, "Thing"));
    
    ASSERT_EQ(true, onto->Subsumes("Thing", "Animal"));
    ASSERT_EQ(true, onto->Subsumes("Animal", "Dog"));
    
    e.AddProperty(Property(Ontology::kType, "Thing"));
    e.AddProperty(Property(Ontology::kType, "Animal"));
    e.AddProperty(Property(Ontology::kType, "Dog"));
    
    type.SetValue("Dog");
    EXPECT_EQ(type, e.Type());
    
    e.ClearProperties();
    EXPECT_EQ(0, e.Size());
    
    e.AddProperty(Property(Ontology::kType, "Dog"));
    e.AddProperty(Property(Ontology::kType, "Thing"));
    e.AddProperty(Property(Ontology::kType, "Animal"));
    
    EXPECT_EQ(type, e.Type());
}

TEST(Entity, IsType) 
{
    Ontology::SetDefault
    (pOntology
     (new Ontology
      ("/Users/roman/Projects/NLP/gre/resources/dbpedia.nt")));
    
    Property type = Property(Ontology::kType, "Dog");
    Entity e, f;
    e.AddProperty(Ontology::kPersonType);
    e.AddProperty(Ontology::kThingType);
    f.AddProperty(Ontology::kThingType);
    
    EXPECT_EQ(Ontology::kPersonType, e.Type());
    EXPECT_EQ(Ontology::kThingType, f.Type());
    
    EXPECT_EQ(true, e.IsType(Ontology::kPersonType));
    EXPECT_EQ(true, e.IsType(Ontology::kThingType));
    EXPECT_EQ(true, f.IsType(Ontology::kThingType));
    
    EXPECT_EQ(false, e.IsType(Property(Ontology::kType, "none")));
    EXPECT_EQ(false, f.IsType(Property(Ontology::kType, "none")));
}

TEST(Entity, Serialisation) 
{
    Ontology::SetDefault(pOntology());
    stringstream ss;
    
    Property type = Property(Ontology::kType, "Dog");
    Entity e;
    e.AddProperty(Property(Ontology::kType, "Thing"));
    e.AddProperty(Property(Ontology::kType, "Animal"));
    e.AddProperty(Property(Ontology::kType, "Dog"));
    e.SetType(type);
    
    ss << e;
    
    Entity f;
    try 
    {
        ss >> f;
    } 
    catch (DeserialisationException& de) 
    {
        cerr << de.what() << "\n\t" << de.data() << endl;
    }
    
    EXPECT_EQ(e, f);
    
    ss.str("");
    std::vector<Entity> entities;
    std::vector<Entity> fentities(3);
    
    for (int i = 0; i < 3; ++i) 
    {
        entities.push_back(e);
    }
    
    for (int i = 0; i < 3; ++i) 
    {
        ss << entities[i] << '\n';
    }
    
    try 
    {
        for (int i = 0; i < 3; ++i) 
        {
            ss >> fentities[i];
        }
    } 
    catch (DeserialisationException& de) 
    {
        cerr << de.what() << "\n\t" << de.data() << endl;
    }
    
    for (int i = 0; i < 3; ++i) 
    {
        EXPECT_EQ(entities[i], fentities[i]);
    }
}

